/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
 * MA  02110-1301, USA.
 *
 * http://www.gnu.org/copyleft/gpl.html
 */
package parser.serverData.npcs;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;

import parser.clientData.clientNpc.Data;
import parser.clientData.clientNpc.NpcClient;
import parser.clientData.clientNpc.TabList;
import parser.clientData.collectedNpcData.NpcStat;
import parser.clientData.goodlist.ClientNpcGoodslist;
import parser.extraData.tradeTabs.TradeTab;
import parser.extraData.tradeTabs.TradeTabsLoader;
import parser.util.DataManager;

/**
 * @author Mr. Poke
 *
 */
public class NpcTemplatesSave
{
	public static void save()
	{
		Map<Integer, TradeTab> tabData = TradeTabsLoader.load();
		ObjectFactory objFactory = new ObjectFactory();
		Map<Integer, ClientNpcGoodslist> goodMapbyId = new HashMap<Integer, ClientNpcGoodslist>();
		for ( ClientNpcGoodslist good:DataManager.getInstance().getGoodMap().values())
		{
			goodMapbyId.put(good.getId(), good);
		}
		JAXBElement<NpcData> collection = objFactory.createNpcTemplates(objFactory.createNpcData());
		List<NpcTemplate> templateList = collection.getValue().getNpcTemplate();
		for (NpcClient npc : DataManager.getInstance().getNpcs())
		{
			String generalTribe = null;
			if (npc.getTribe() == null)
				System.out.println("Missing tribe: "+npc.getId() );
			else
				generalTribe = DataManager.getInstance().getBaseTribe(npc.getTribe().toUpperCase());
			NpcTemplate npcTemplate = null;
			if (npc.getUiType() != null && npc.getUiType().equalsIgnoreCase("Binding_Stone"))
			{
				npcTemplate = new NpcTemplate();
			}
			else if (npc.getAiName() != null && npc.getAiName().equals("Postbox"))
			{
				npcTemplate = new NpcTemplate();
			}
			else if (npc.getAiName() != null && npc.getAiName().equals("SummonServant"))
			{
				npcTemplate = new CreatureTemplate();
			}
			else if (npc.getUiType() != null && npc.getUiType().equalsIgnoreCase("Trap"))
			{
				npcTemplate = new CreatureTemplate();
			}
			else if (npc.getUiType() != null && npc.getUiType().equalsIgnoreCase("summoned"))
			{
				npcTemplate = new CreatureTemplate();
			}
			else if (npc.getBindstoneType() != null)
			{
				npcTemplate = new KiskTemplate();
			}
			else if (generalTribe == null)
			{
				npcTemplate = new CreatureTemplate();
			}
			else if (npc.getCursorType() != null && npc.getCursorType().equals("action"))
			{
				npcTemplate = new ActionItemTemplate();
			}
			else if (generalTribe.equalsIgnoreCase("MONSTER"))
			{
				npcTemplate = new CreatureTemplate();
			}
			else if (generalTribe.equalsIgnoreCase("GUARD")||
					generalTribe.equalsIgnoreCase("guard_Dark")||
					generalTribe.equalsIgnoreCase("guard_Dragon"))
			{
				npcTemplate = new CreatureTemplate();
			}
			else if (generalTribe.equalsIgnoreCase("GENERAL")||
					generalTribe.equalsIgnoreCase("GENERAL_Dark")||
					generalTribe.equalsIgnoreCase("GENERAL_Dragon"))
			{
				npcTemplate = new CreatureTemplate();
			}
			else
			{
				npcTemplate = new CreatureTemplate();
				//System.out.println(generalTribe+" "+npc.getId());
			}
			if (npc.getBindstoneType() != null)
			{
				npcTemplate = new KiskTemplate();
				KiskTemplate kisk = (KiskTemplate)npcTemplate;
				kisk.setResurrects(npc.getBindstoneUsecount());
				kisk.setMembers(npc.getBindstoneCapacity());
				if (npc.getBindstoneType().equals("party"))
					kisk.setKisktype(EKiskType.GROUP);
				else if (npc.getBindstoneType().equals("normal"))
					kisk.setKisktype(EKiskType.RACE);
				else if (npc.getBindstoneType().equals("guild"))
					kisk.setKisktype(EKiskType.LEGION);
				else if (npc.getBindstoneType().equals("force"))
					kisk.setKisktype(EKiskType.ALLIANCE);
				else if (npc.getBindstoneType().equals("private"))
					kisk.setKisktype(EKiskType.SOLO);
			}
			else if (npcTemplate instanceof ActionItemTemplate)
			{
				ActionItemTemplate actionItemTemplate = (ActionItemTemplate)npcTemplate;
				if (npc.getUserAnimation() != null)
				{
					if (npc.getUserAnimation().equals("quest_actsitting"))
						actionItemTemplate.setUserAnimation("START_QUESTLOOT");
					else if (npc.getUserAnimation().equals("quest_energyblow"))
						actionItemTemplate.setUserAnimation("START_QUESTLOOT"); //TODO EMOTION ID....
					else if (npc.getUserAnimation().equals("quest_actstanding"))
						actionItemTemplate.setUserAnimation("START_QUESTLOOT"); //TODO EMOTION ID....
					else if (npc.getUserAnimation().equals("quest_energydrain"))
						actionItemTemplate.setUserAnimation("START_QUESTLOOT"); //TODO EMOTION ID....
					else if (npc.getUserAnimation().equals("quest_lightupfire"))
						actionItemTemplate.setUserAnimation("START_QUESTLOOT"); //TODO EMOTION ID....
					else if (npc.getUserAnimation().equals("quest_flute"))
						actionItemTemplate.setUserAnimation("START_QUESTLOOT"); //TODO EMOTION ID....
					else if (npc.getUserAnimation().equals("quest_readbook"))
						actionItemTemplate.setUserAnimation("START_QUESTLOOT"); //TODO EMOTION ID....
					else if (npc.getUserAnimation().equals("quest_splashwater"))
						actionItemTemplate.setUserAnimation("START_QUESTLOOT"); //TODO EMOTION ID....
					//else
						//System.out.println("Missing emotion type: "+ npc.getId()+ " "+npc.getUserAnimation());
				}
				if (npc.getTalkDelayTime() != 0)
					actionItemTemplate.setTalkDelayTime(npc.getTalkDelayTime()*1000);
			}
			else
			{
				npcTemplate = new CreatureTemplate();

			}
			FunctionData fData = new FunctionData();
			boolean hashFunction = false;
			if (npc.getAirlinesName() != null)
			{
				fData.setAirlineId(DataManager.getInstance().getClientAirline().get(npc.getAirlinesName().toUpperCase()).getId());
				hashFunction = true;
			}
			if (npc.getTradeInfo() != null)
			{
				BuyListData tradeInfo = new BuyListData();
				tradeInfo.setType((byte) 1);
				if (npc.getTradeInfo().getBuyPriceRate() != null)
					tradeInfo.setBuyPriceRate(npc.getTradeInfo().getBuyPriceRate()/10);
				if (npc.getTradeInfo().getSellPriceRate() != null)
					tradeInfo.setSellPriceRate(npc.getTradeInfo().getSellPriceRate()/10);
				TabList tabs = npc.getTradeInfo().getTabList();
				if (tabs != null)
				{
					for (Data data : tabs.getData())
					{
						int id = DataManager.getInstance().getGoodMap().get(data.getTab().toUpperCase()).getId();
						tradeInfo.getIds().add(id);
					}
				}
				hashFunction = true;
				fData.setBuyListData(tradeInfo);
			}
			
			if (npc.getAbyssTradeInfo() != null)
			{
				BuyListData tradeInfo = new BuyListData();
				tradeInfo.setType((byte) 2);
				if (npc.getAbyssTradeInfo().getBuyPriceRate() != null)
					tradeInfo.setBuyPriceRate(npc.getAbyssTradeInfo().getBuyPriceRate()/10);
				if (npc.getAbyssTradeInfo().getSellPriceRate() != null)
					tradeInfo.setSellPriceRate(npc.getAbyssTradeInfo().getSellPriceRate()/10);
				TabList tabs = npc.getAbyssTradeInfo().getTabList();
				if (tabs != null)
				{
					for (Data data : tabs.getData())
					{
						int id = DataManager.getInstance().getGoodMap().get(data.getAtab().toUpperCase()).getId();
						tradeInfo.getIds().add(id);
					}
				}
				hashFunction = true;
				fData.setBuyListData(tradeInfo);
			}
		
			if (npc.getCouponTradeInfo() != null)
			{
				BuyListData tradeInfo = new BuyListData();
				tradeInfo.setType((byte) 3);
				if (npc.getCouponTradeInfo().getBuyPriceRate() != null)
					tradeInfo.setBuyPriceRate(npc.getCouponTradeInfo().getBuyPriceRate()/10);
				if (npc.getCouponTradeInfo().getSellPriceRate() != null)
					tradeInfo.setSellPriceRate(npc.getCouponTradeInfo().getSellPriceRate()/10);
				TabList tabs = npc.getCouponTradeInfo().getTabList();
				if (tabs != null)
				{
					for (Data data : tabs.getData())
					{
						int id = DataManager.getInstance().getGoodMap().get(data.getCtab().toUpperCase()).getId();
						tradeInfo.getIds().add(id);
					}
				}
				hashFunction = true;
				fData.setBuyListData(tradeInfo);
			}

			if (npc.getExtraCurrencyTradeInfo() != null)
			{
				BuyListData tradeInfo = new BuyListData();
				tradeInfo.setType((byte) 4);
				if (npc.getExtraCurrencyTradeInfo().getBuyPriceRate() != null)
					tradeInfo.setBuyPriceRate(npc.getExtraCurrencyTradeInfo().getBuyPriceRate()/10);
				if (npc.getExtraCurrencyTradeInfo().getSellPriceRate() != null)
					tradeInfo.setSellPriceRate(npc.getExtraCurrencyTradeInfo().getSellPriceRate()/10);
				TabList tabs = npc.getExtraCurrencyTradeInfo().getTabList();
				if (tabs != null)
				{
					for (Data data : tabs.getData())
					{
						ClientNpcGoodslist good = DataManager.getInstance().getGoodMap().get(data.getEtab().toUpperCase());
						if (good == null)
						{
							System.out.println(data.getEtab());
							continue;
						}
						int id = DataManager.getInstance().getGoodMap().get(data.getEtab().toUpperCase()).getId();
						tradeInfo.getIds().add(id);
					}
				}
				hashFunction = true;
				fData.setBuyListData(tradeInfo);
			}

			if (npcTemplate.getFunctionData() == null || npcTemplate.getFunctionData().getBuyListData() == null)
			{
				TradeTab data = tabData.get(npc.getId());
				if (data != null)
				{
					BuyListData tradeInfo = new BuyListData();
					ClientNpcGoodslist goods = goodMapbyId.get(data.getTabs().get(0));
					if (goods != null)
					{
						Integer category = goodMapbyId.get(data.getTabs().get(0)).getUseCategory();
						if (category == null)
							category = 0;
						tradeInfo.setType((byte) (category+1));
						tradeInfo.getIds().addAll(data.getTabs());
						hashFunction = true;
						fData.setBuyListData(tradeInfo);
					}
					else
						System.out.println(data.getTabs().get(0));
				}
			}
			if (hashFunction)
			{
				npcTemplate.setFunctionData(fData);
			}
			NpcCommonData commonData = new NpcCommonData();
			if (npc.getVisibleEquipments() != null)
			{
				short mask = 0;
				if (npc.getVisibleEquipments().getMain() != null)
				{
					try
					{
						int itemId = DataManager.getInstance().getItemNameIdMap().get(npc.getVisibleEquipments().getMain().toLowerCase());
						mask = (short) (mask | 1);
						commonData.getEquipment().add(itemId);
					}
					catch (Exception e)
					{
						System.out.println("Missing item: "+npc.getId()+" "+npc.getVisibleEquipments().getMain());
					}
				}
				if (npc.getVisibleEquipments().getSub() != null)
				{
					try
					{
						int itemId = DataManager.getInstance().getItemNameIdMap().get(npc.getVisibleEquipments().getSub().toLowerCase());
						mask = (short) (mask | (1 << 1));
						commonData.getEquipment().add(itemId);
					}
					catch (Exception e)
					{
						System.out.println("Missing item: "+npc.getId()+" "+npc.getVisibleEquipments().getSub());
					}
				}
				if (npc.getVisibleEquipments().getHead() != null)
				{
					try
					{
						int itemId = DataManager.getInstance().getItemNameIdMap().get(npc.getVisibleEquipments().getHead().toLowerCase());
						mask = (short) (mask | (1 << 2));
						commonData.getEquipment().add(itemId);
					}
					catch (Exception e)
					{
						System.out.println("Missing item: "+npc.getId()+" "+npc.getVisibleEquipments().getHead());
					}
				}
				if (npc.getVisibleEquipments().getTorso() != null)
				{
					try
					{
						int itemId = DataManager.getInstance().getItemNameIdMap().get(npc.getVisibleEquipments().getTorso().toLowerCase());
						mask = (short) (mask | (1 << 3));
						commonData.getEquipment().add(itemId);
					}
					catch (Exception e)
					{
						System.out.println("Missing item: "+npc.getId()+" "+npc.getVisibleEquipments().getTorso());
					}
				}
				if (npc.getVisibleEquipments().getGlove() != null)
				{
					try
					{
						int itemId = DataManager.getInstance().getItemNameIdMap().get(npc.getVisibleEquipments().getGlove().toLowerCase());
						mask = (short) (mask | (1 << 4));
						commonData.getEquipment().add(itemId);
					}
					catch (Exception e)
					{
						System.out.println("Missing item: "+npc.getId()+" "+npc.getVisibleEquipments().getGlove());
					}
				}
				if (npc.getVisibleEquipments().getFoot() != null)
				{
					try
					{
						int itemId = DataManager.getInstance().getItemNameIdMap().get(npc.getVisibleEquipments().getFoot().toLowerCase());
						mask = (short) (mask | (1 << 5));
						commonData.getEquipment().add(itemId);
					}
					catch (Exception e)
					{
						System.out.println("Missing item: "+npc.getId()+" "+npc.getVisibleEquipments().getFoot());
					}
				}
				if (npc.getVisibleEquipments().getShoulder() != null)
				{
					try
					{
						int itemId = DataManager.getInstance().getItemNameIdMap().get(npc.getVisibleEquipments().getShoulder().toLowerCase());
						mask = (short) (mask | (1 << 11));
						commonData.getEquipment().add(itemId);
					}
					catch (Exception e)
					{
						System.out.println("Missing item: "+npc.getId()+" "+npc.getVisibleEquipments().getShoulder());
					}
				}
				if (npc.getVisibleEquipments().getLeg() != null)
				{
					try
					{
						int itemId = DataManager.getInstance().getItemNameIdMap().get(npc.getVisibleEquipments().getLeg().toLowerCase());
						mask = (short) (mask | (1 << 12));
						commonData.getEquipment().add(itemId);
					}
					catch (Exception e)
					{
						System.out.println("Missing item: "+npc.getId()+" "+npc.getVisibleEquipments().getLeg());
					}
				}
				commonData.setEquipmentMask(mask);
			}

			String gameLang = npc.getGameLang();
			if (gameLang != null)
			{
				gameLang = gameLang.toLowerCase();
			
				if (gameLang.equals("light"))
					commonData.setGameLang(GameLang.ELYOS);
				else if (gameLang.equals("dark"))
					commonData.setGameLang(GameLang.ASMODIANS);
				else if (gameLang.equals("dragon"))
					commonData.setGameLang(GameLang.DRAGON);
				else if (gameLang.equals("krall"))
					commonData.setGameLang(GameLang.KRALL);
				else if (gameLang.equals("lycan"))
					commonData.setGameLang(GameLang.LYCAN);
				else if (gameLang.equals("dev_normal_40") ||gameLang.equals("1000")||gameLang.equals("1"))
				{
					//TODO NCSOFT bug ?:O
				}
				else
					System.out.println("Missing game_lang: "+npc.getId()+" "+gameLang);
			}

			if (npc.getBoundRadius() != null)
			{
				BoundRadius boundRadius = new BoundRadius();
				boundRadius.setFront(npc.getBoundRadius().getFront());
				boundRadius.setSide(npc.getBoundRadius().getSide());
				boundRadius.setUpper(npc.getBoundRadius().getUpper());
				npcTemplate.setBoundRadius(boundRadius);
			}

			npcTemplate.setName(DataManager.getInstance().getStringNameMap().get(npc.getDesc().toLowerCase()).getBody());
			commonData.setNameId(DataManager.getInstance().getStringNameMap().get(npc.getDesc().toLowerCase()).getId());
			if (npc.getRaceType() == null)
				commonData.setRace(Race.NONE);
			else if (npc.getRaceType().toUpperCase().equals("PC_DARK"))
				commonData.setRace(Race.ASMODIANS);
			else if (npc.getRaceType().toUpperCase().equals("PC_LIGHT"))
				commonData.setRace(Race.ELYOS);
			else
				commonData.setRace(Race.valueOf(npc.getRaceType().toUpperCase().trim()));
			
			switch (npc.getHpgaugeLevel())
			{
				case 0:
				case 1:
					commonData.setRank(NpcRank.NOVICE);
					commonData.setRating(NpcRating.JUNK);
					break;
				case 2:
					commonData.setRank(NpcRank.NOVICE);
					commonData.setRating(NpcRating.NORMAL);
					break;
				case 3:
					commonData.setRank(NpcRank.DISCIPLINED);
					commonData.setRating(NpcRating.NORMAL);
					break;
				case 4:
					commonData.setRank(NpcRank.SEASONED);
					commonData.setRating(NpcRating.NORMAL);
					break;
				case 5:
					commonData.setRank(NpcRank.EXPERT);
					commonData.setRating(NpcRating.NORMAL);
					break;
				case 10:
					commonData.setRank(NpcRank.NOVICE);
					commonData.setRating(NpcRating.ELITE);
					break;
				case 11:
					commonData.setRank(NpcRank.DISCIPLINED);
					commonData.setRating(NpcRating.ELITE);
					break;
				case 12:
					commonData.setRank(NpcRank.SEASONED);
					commonData.setRating(NpcRating.ELITE);
					break;
				case 13:
					commonData.setRank(NpcRank.EXPERT);
					commonData.setRating(NpcRating.ELITE);
					break;
				case 14:
					commonData.setRank(NpcRank.VETERAN);
					commonData.setRating(NpcRating.ELITE);
					break;
				case 20:
					commonData.setRank(NpcRank.EXPERT);
					commonData.setRating(NpcRating.HERO);
					break;
				case 21:
					commonData.setRank(NpcRank.VETERAN);
					commonData.setRating(NpcRating.HERO);
					break;
				case 22:
					commonData.setRank(NpcRank.MASTER);
					commonData.setRating(NpcRating.HERO);
					break;
				case 26:
					commonData.setRank(NpcRank.EXPERT);
					commonData.setRating(NpcRating.LEGENDARY);
					break;
				case 27:
					commonData.setRank(NpcRank.VETERAN);
					commonData.setRating(NpcRating.LEGENDARY);
					break;
				default:
					System.out.println("Missing hpGuage: "+npc.getId()+" "+npc.getHpgaugeLevel());
			}
			if (npc.getTribe() != null)
				commonData.setTribe(npc.getTribe().toUpperCase());
			
			NpcStat collectedData = DataManager.getInstance().getCollectedNpcData(npc.getId());
		
			if (collectedData != null && collectedData.getLvl() != 0)
			{
				npcTemplate.setLevel(collectedData.getLvl());
			}
			else if (npc.getName() != null)
			{
				StringTokenizer st = new StringTokenizer(npc.getName().toLowerCase(), "_");
				if (st.countTokens() >0)
				{
					String[] strings = new String[st.countTokens()];
					int i=0;
					int lvl = 0;
					while (st.hasMoreTokens())
					{
						String str = st.nextToken();
						if (str.startsWith("lv"))
						{
							try
							{
								lvl = Integer.valueOf(str.substring(2));
								break;
							}
							catch (Exception e)
							{
								// TODO: handle exception
							}
						}
						strings[i++] = str;
					}
					if (lvl == 0)
					{
						try
						{
							lvl = Integer.valueOf(strings[strings.length - 2]);
						}
						catch (Exception e)
						{
						}
					}
					if (lvl > 0 && lvl < 99)
					{
						npcTemplate.setLevel((byte) lvl);
					}
				}
			}
				npcTemplate.setCommonData(commonData);
				if (collectedData != null && collectedData.getTitleId() != 0)
				{
					commonData.setTitleId(collectedData.getTitleId());
				}
				if (npcTemplate instanceof CreatureTemplate)
				{
					byte lvl = npcTemplate.getLevel();
					int hp = lvl*lvl*5+ 35*lvl+138;
					
					float multi = 1;
					//TODO 
					switch (commonData.getRating())
					{
						case JUNK:
							multi *= 0.1f;
							break;
						case NORMAL:
							break;
						case ELITE:
							multi *=5;
							break;
						case HERO:
							multi *=50;
							break;
						case LEGENDARY:
							multi *=150;
					}
					switch (commonData.getRank())
					{
						case NOVICE:
						case DISCIPLINED:
							break;
						case SEASONED:
							multi *= 1.075;
							break;
						case EXPERT:
							multi *= 1.375;
							break;
						case VETERAN:
							multi *= 5;
							break;
						case MASTER:
							multi *= 10;
					}
					hp = (int) (hp*multi);
					if (collectedData != null && collectedData.getHp() != 0)
					{
						hp = collectedData.getHp();
					}
					CreatureStatData statData = new CreatureStatData();
					statData.setHp(hp);
					statData.setMp(lvl*lvl*5+ 35*lvl+138);
					if (commonData.getRating() == NpcRating.JUNK)
						statData.setExp(0);
					else
						statData.setExp(statData.getHp()*2);
					
					if (npc.getMoveSpeedCombatRun() != 0)
						statData.setMoveSpeedCombatRun(npc.getMoveSpeedCombatRun());
					if (npc.getMoveSpeedNormalRun() != 0)
						statData.setMoveSpeedNormalRun(npc.getMoveSpeedNormalRun());
					if (npc.getMoveSpeedNormalWalk() != 0)
						statData.setMoveSpeedNormalWalk(npc.getMoveSpeedNormalWalk());
					if (npc.getAttackDelay() != 0)
						statData.setAttackDelay(npc.getAttackDelay());
					if (npc.getAttackRange() != 0)
						statData.setAttackRange(npc.getAttackRange());
					if (npc.getSensoryRange() != 0)
						statData.setSensoryRange(npc.getSensoryRange());
					if (npc.getAmmoSpeed() != 0)
						statData.setAmmoSpeed(npc.getAmmoSpeed());
					if (npc.getAttackRate() != 0)
						statData.setAttackRate(npc.getAttackRate());
					
					
					((CreatureTemplate)npcTemplate).setCreatureStatData(statData);
				}

			npcTemplate.setNpcId(npc.getId());
			
			if (npc.getTalkingDistance() > 0)
				npcTemplate.setTalkingDistance((int)npc.getTalkingDistance());
			
			templateList.add(npcTemplate);
		}
		
		try
		{
			JAXBContext jaxbContext = JAXBContext.newInstance("parser.serverData.npcs");
			Marshaller marshaller = jaxbContext.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "npc_templates.xsd");
			marshaller.marshal(collection, new FileOutputStream("static_data/npcs/npc_templates.xml"));
		}
		catch (PropertyException e)
		{
			e.printStackTrace();
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
		catch (JAXBException e)
		{
			e.printStackTrace();
		}
	}
}
